﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Gamemaster
{
    public class RollResults
    {
        public int Quantity { get; set; }
        public int Sides { get; set; }
        public int Bonus { get; set; }
        public int Penalty { get; set; }
        public List<int> Results { get; set; }
        public int LossDueToBotches { get; set; }
        public int Value
        {
            get { return Results.Sum().Plus(Bonus).Minus(Penalty).Minus(LossDueToBotches); }
        }

        public RollResults()
        {
            Results = new List<int>();
            Quantity = 1;
            Sides = 6;
        }
        public RollResults(int quantity) : this()
        {
            Quantity = quantity;
            Reroll();
        }
        public RollResults(int quantity, int sides) : this(quantity)
        {
            Sides = sides;
            Reroll();
        }

        public int Reroll()
        {
            Results.Clear();

            for (int i = 0; i < Quantity; i++)
            {
                int roll = Roll.d(Sides);

                Results.Add(roll);
            }

            return Value;
        }
        public int Reroll(int sides)
        {
            Sides = sides;
            
            return Reroll();
        }
        public int Reroll(int quantity, int sides)
        {
            Quantity = quantity;
            Sides = sides;
            
            return Reroll();
        }

        public RollResults Plus(int bonus)
        {
            Bonus = bonus;

            return this;
        }
        public RollResults Minus(int penalty)
        {
            Penalty = penalty;

            return this;
        }
        public RollResults Explode()
        {
            for (int i = 0; i < Results.Count(r => r == Sides); i++)
            {
                int explosionRoll;

                do
                {
                    explosionRoll = Roll.d(Sides);
                    Results.Add(explosionRoll);
                }
                while(explosionRoll == Sides);
            }

            return this;
        }
        public RollResults Wild()
        {
            if (Results.Last() == Sides)
            {
                int explosionRoll;

                do
                {
                    explosionRoll = Roll.d(Sides);
                    Results.Add(explosionRoll);
                }
                while (explosionRoll == Sides);
            }

            return this;
        }
        public RollResults Botch()
        {
            IEnumerable<int> successes = Results.Where(r => r != 1).OrderByDescending(r => r);

            for (int i = 0; i < Results.Count(r => r == 1); i++)
            {
                if (successes.Count() == 0)
                {
                    break;
                }

                successes = successes.Skip(1);
            }

            LossDueToBotches = Results.Sum() - successes.Sum();

            return this;
        }
        public RollResults BotchWild()
        {
            if (Results.LastOrDefault() == 1)
            {
                IEnumerable<int> successes = Results.Take(Results.Count() - 1).OrderByDescending(r => r);

                if (successes.Count() > 0)
                {
                    successes = successes.Skip(1);
                }

                LossDueToBotches = Results.Sum() - successes.Sum();
            }

            return this;
        }

        public override string ToString()
        {
            return ToString(ResultsFormat.SingleValue);
        }
        public string ToString(ResultsFormat format)
        {
            string set = format == ResultsFormat.ShowDice ? Results.ToResultsString() : string.Empty;

            return string.Format("{0}d{1}{2}{3}: {4} {5}" 
                ,Quantity                       // 0
                ,Sides                          // 1
                ,Bonus.ToBonusString()          // 2
                ,Penalty.ToPenaltyString()      // 3
                ,Value                          // 4
                ,set                            // 5
            );
        }
    }
}
